//GraphicsManager.h
//Created 18/08/13 (Modified for Tile Based Forward Rendering: 24/09/13) 
//Created By Daniel Bowler
//
//The first job of this class is to handle the creation of the device, context
//(immediate) and required buffers for tile based forward rendering.
//
//This class will be similar to D3DApp in my D3D11RenderingDemoApp. Though,
//just the D3D11 aspect of this class rather than handle any Win32 specific
//stuff. 
//
//This engine will implement a Tile Based Forward Renderer as its underlying shading system. 
//It is a modification to traditional forward rendering systems that implements a light culling
//stage on the GPU (GPGPU - thus DirectX11 class hardware is required). In addition, we will be 
//implementing a simple Z-Pre pass system for an efficiency boost (And it makes our algorithm
//just that little bit more complex & interesting to talk about)
//
//Will support: A) Alpha Blended Objects (Transparancy) and B) MSAA 'out of the box'. Both have
//negative issues surounding them (especially Alpha blending - we cant cull lights as effectivly
//anymore (there is an expensive solution which we do not use).
//That said, 99% of the time, you would have this as a perfectly fine trade off)
//
//Note: There is an issue when MSAA is enabled and you move in to fullscreen mode - 
//a few Unhandled Exception warnings crop up (com error?) - Seems fairly low level and some
//people on the MDSN forums seem to think it has something to do with an outdated
//sdk. It doesnt seem to be a major issue to be honest - so I shall ignore it :)
//
//Version 2: Now has seperate Light List Inded Buffers and Light List Start End Buffers for opaque
//and transparant objects. 

#pragma once

//Include needed D3D related headers.
#include "d3dUtil.h"

//
//Defines for the Tile Forward Renderer.
//
//***The following should match those found in TileForwardGlobals.fx***
//
//Size in pixels of each tile for the tile based forward renderer. Will be used
//to dispatch the compute shader. TilesSize = [TILE_PIXEL_RESOLUTION][TILE_PIXEL_RESOLUTION].
//Using the screen size, we can work out how many tiles are needed to cover the screen. 
//
//IMPORTANT: You MUST change the #define (same name) in the TileForwardGlobals.fx. And then
//you MUST recompile the TileForwardLightCullCS.fx (Make any change and recompile) AND the
//TexturedFullScreenQuad.fx (If you are going to visaluse the buffer!!!). (And GraphicsManager.cpp)
//After every change!
//
//Actually.. Any changes to these defines, just rebuild the entire solution!
#define TILE_PIXEL_RESOLUTION 32
//Maximum lights per tile - The light list index buffer will be created at initilisation
//time and can not dynamically change size at run time. This define will allow us to allocate
//enough memory incase each tile will have MAX_LIGHTS_PER_TILE lights assigned to it. 
//
//Note: The light list buffer (global buffer of ALL scene lights) will be able to be modified
//at run time - eg, a change of scene. We could extend this to Light List Index Buffer if we so
//choose (each scene has a differnt max lights per tile value), but to be honest, theres no real
//point in this one scene demo. Doing it this way just cleans up the code a little. 
#define MAX_LIGHTS_PER_TILE 256

//Maximum directional lights - matches value in the TileForwardGlobals.fx file.
#define MAX_DIRECTIONAL_LIGHT_COUNT 3

//General defines
//
//Format of the back buffer - either DXGI_FORMAT_R16G16B16A16_FLOAT
//, DXGI_FORMAT_R8G8B8A8_UNORM or DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
//
//Former causes issues in full screen mode - unsure why. Latter works the best
#define BACKBUFFER_FORMAT DXGI_FORMAT_R8G8B8A8_UNORM

//
//Forward Declarations
//
//Forward declare Application - we will request client width / height
//at some point - and the ShaderManager (Graphics manager creates and clears up)
class Application;
class ShaderManager;
//Forward declare VirtualCamera. Contains view and projection matricies.
class VirtualCamera;
//Forward declare quad effect - used to render certain buffers that need visualising
//(eg, the depth buffer (after the depth pre pass) or the Light List Index Buffer to show
//how many lights affect a given tile). 
class TexturedFullScreenQuadFX;
//Forward declare our tile based culling compute shader effect. The CSFX that will be
//used to cull lights - ie, generate our LLIB and LLSEB. 
class TileForwardLightCullCSFX;
//Forward declare effect used to render lights
class DebugDrawLightsFX;

class GraphicsManager
{
	//Friend declaration of my Application class. This class (Application)
	//will call OnResize(), Release() and Init(). This prevents other
	//classes from accidently calling these functions (And looks good!)
	friend Application;
public:
	//Returns a pointer to our shared (singleton) instance of the graphics manager.
	static GraphicsManager* GetInstance();

	//Function that returns the aspect ratio (backbuffer)
	float ReturnBackBufferAspectRatio() {return backBufferAspect;};
	//Functions that return the width and height of the window
	unsigned ReturnBackBufferWidth() {return backBufferWidth;};
	unsigned ReturnBackBufferHeight() {return backBufferHeight;};

	//Returns useful D3D11 resources such as the device, context, etc. 
	ID3D11Device*           D3DDevice() {return d3dDevice;};
	ID3D11DeviceContext*    D3DImmediateContext() {return d3dImmediateContext;};
	IDXGISwapChain*         D3DSwapChain() {return swapChain;};
	ID3D11RenderTargetView* D3DBackBufferRenderTargetView() {return backBufferRenderTargetView;};
	ID3D11DepthStencilView* D3DBackBufferDepthStencilView() {return depthStencilView;};
	D3D11_VIEWPORT          D3DFullScreenViewport(){return screenViewport;};

public:
	//MSAA Functionality is 'out of the box' supported in Forward
	//renderers. Thus we support it - and do so allowing runtime 
	//changes.
	//
	//We support 4x msaa only. No need for 8x + (So we dont need to ask
	//the graphics card if 8x is supported. 4x WILL be supported
	//by ALL DX11 class hardware - and its good enough for our purposes!)
	//
	//Enable/Disable MSAA - Dont do mid frame
	void Enable4xMSAA();
	void Disable4xMSAA();
	//Is MSAA Enabled?
	bool IsMSAAEnabled(){return msaaEnabled;};
	//Returns the cards maximum MSAA (4x) quality -1. Use this function
	//to set the MSAA quality for a texture when you are creating it. 
	UINT GetMSAAQuality(){return msaaQuality;};

public:
	//Functions to clear buffers - should be called at the top of each
	//scenes DrawScene() function.
	//
	//Bind and clear backbuffer (Black)
	void BindAndClearBackBuffer();
	//Alternativly, you can choose the colour.
	//Binds the standard depth/stencil view. Anything fancy needs to be done directly with the
	//api (eg, custom depth/stencil views for shadow mapping).
	void BindAndClearBackBufferColour(const float* col);
	//Rebinds the backbuffer without clearing.
	//Binds the standard depth/stencil view. Anything fancy needs to be done directly with the
	//api (eg, custom depth/stencil views for shadow mapping).
	void BindBackBuffer();
	//Clears depth stencil view -default values should do fine.
	void ClearDepthStencilView(float depthValue = 1.0f, UINT8 stencilValue = 0);
	
	//Present (flip) the backbuffer. Pass True when you want vsync enabled (60fps)
	void Present(bool vysnc = false);

public:
	//Tile Based Forward Rendering specific functions. 
	//
	//Stage 1) Z Pre Pass Stage.
	//
	//Function that begins the Z pre pass stage. Ie, restores the default
	//depth stencil state (You can override that if needed - eg you have
	//revered the depth test in your application.) and disables colour
	//writes by unbinding the RTV. It will also bind the standard depth buffer.
	//
	//You can then render objects as normal and they will fill the depth buffer.
	void BeginZPrePassStage();

	//
	//Stage 2) Light Culling Stage
	//
	//Sets the lights for this scene - should NOT be called per frame as we release
	//and recreate the light list buffer each time this function is called. The whole
	//idea of this algorithm is to remove the need to compute which lights affect this frame
	//(or object) - passing such responsability to the light culling stage. Instead
	//you init all lights for a scene and allow the light culling CS to decide
	//which lights affect each tile. Thus, you should only call this function
	//when you change scenes (OpenScene()). 
	//
	//Note: Point and Spot lights ONLY. Directional lights are uploaded to
	//each shader per frame (For simplicity) via Cbuffers. (2/3 max usually) since they
	//will affect every tile anyway and so dont benifit from culling. Semi-inefficent, but oh
	//well... this isnt a perfect solution because we need to retain some backwards compatability.
	//
	//Note: Added 14/10/13 - If you pass true as the third param, the function will
	//go ahead and create the buffers required to render the lights to the screen.
	//
	//Note: If you want moving/changing lights, then you will have to call this function every
	//frame or so and pass the entire (updated) lights (even those not updated). This function
	//will release the previous buffers and reallocate data. This is, unfortunatly, slow...
	//However, for scenes with say 250 lights (Representation of a true game scene)
	//, then it only takes 0.5ms to do this so no real issue.
	void SetSceneLights(TileBasedLight* lightArray, unsigned count, bool shouldCreateVisBuffers);
	//Modified: 25/10/13 - We now better support dynamic (changing) lights. This function uses the
	//mapping component of the API and updates the lights.
	//
	//If you have a completly new set of data, then SetSceneLights() is the function for
	//you. 
	//
	//We do update all lights in this process (But quicker than the above process), so it
	//is somewhat slow if all you need to do is move one light. However, its not too bad for
	//small numbers of lights in a scene (200 odd)
	//
	//To turn off a light with this method, just change the colour of the light (amb, diff and spec)
	//to (0,0,0,1). If you prefer, just take that light out of the array and use the SetSceneLights
	//function. 
	void UpdateSceneLights(TileBasedLight* newLightArray, bool shouldCreateVisBuffers);
	//Preform the light culling. Note that the scene lights MUST be set for this to occur. Pass
	//the main camera (eg, player cam). Thus will be the camera that you have used to obtain
	//view and proj matricies when rendering each game object. 
	void PreformLightCull(VirtualCamera* mainCam);
	
	//
	//Data to be passed to our effect (Stage 3) shaders.
	//
	//Returns the SRV to the Light List Buffer (LLB) (Created & inited through SetSceneLights())
	ID3D11ShaderResourceView* ReturnLightListBufferSRV(){return lightListBufferSRV;};
	//Returns the SRV to the Light List Index Buffer (LLIB). 
	ID3D11ShaderResourceView* ReturnLightListIndexBufferSRV(){return lightListIndexBufferSRV;};
	//Returns the SRV to the Light List Start/End Buffer (LLSEB).
	ID3D11ShaderResourceView* ReturnLightListStartEndBufferSRV(){return lightListStartEndBufferSRV;};
	//Light List Index Buffer Transparant (LLIBT)
	ID3D11ShaderResourceView* ReturnLightListIndexBufferTransparantSRV(){return lightListIndexBufferTSRV;};
	//Light List Start End Buffer Transparant (LLSEBT)
	ID3D11ShaderResourceView* ReturnlightListStartEndBufferTransparantSRV(){return lightListStartEndBufferTSRV;};
	//
	//Other getters
	//
	//Returns the number of lights
	unsigned ReturnNumberOfActiveLights(){return lightCount;};

	//Stage 3) Shading Stage.
	//
	//Enables colour writes (Opaque objects ONLY), bind the backbuffer resets any render states 
	//and sets up the depth test for colour (shading) writies - 
	//as we have a z pre pass stage, we need to change the depth test to ==.
	//
	//Note that you can NOT draw transparant objects with this depth test. You have to change
	//it to < again - Functionality will be provided to handle this.
	void BeginOpaqueShadingStage();
	//And this one allows you to draw transparant objects. Reverts the depth
	//test to the default <
	void BeginTransparantShadingStage();

public:
	//Tile Based Forward Rendering Debug Functions
	//
	//Renders the depth buffer filled after the Z Pre Pass. 
	void DrawZPrePassFilledDepthBuffer();
	//Samples the Maz Z buffer. Each pixel in a tile will be the same colour. 
	void DrawTileMaxZBuffer();
	//Passes the LightListStartEndBuffer - This will show us how many lights have been 
	//assigned to each tile. This function will render in greyscale. 
	void DrawLightListStartEndBufferGreyscale();
	//This is as above, but using differnt colours to represent how many lights
	//have been assigned to a tile. This is less accurate than above, but easier
	//to see areas of high and low density.
	void DrawLightListStartEndBufferColour();
	//
	//Modified: 24/10/13:
	//
	//Like the above two functions, but shows the LLSEB(T) - Ie, how many lights
	//are affecting a tile for transparant geometry. 
	void DrawLightListStartEndBufferTransparantGreyscale();
	void DrawLightListStartEndBufferTransparantColour();
	//
	//Renders spheres to indicate the locations of the lights. Note: Stores then
	//resets blend state/stencil state so its fairly safe to call at any time. But, 
	//for accurate results, call after you have rendered your opaque geometry 
	//but before calling GraphicsManager::BeginTransparantShadingStage()
	void DrawLights(VirtualCamera* cam);

private:
	//Functions called by Application - it would be a bit unpleasent if other classes
	//were to call them!
	//
	//Called when the window is resized. Will have to manage some Direct3D
	//resources. Also has a use interally to initially create our D3D
	//resources (eg, the backbuffer, etc). This is to avoid code duplication. 
	void OnResize();
	//Releases the Graphics manager and D3D11 resources - Called by the Application. 
	void Release();
	//Inits Device, context and resources - Called by the application.
	void Init(HWND mainWnd);

private:
	//Pointer to our shared application - we will, for example, ensure that
	//our backbuffer/dsv will be the same size as the client height/width - that
	//the Application stores. 
	Application* sharedApplication;
	//Pointer to shader manager
	ShaderManager* sharedShaderManager;

	//Backbuffer data
	float backBufferAspect;
	unsigned backBufferWidth;
	unsigned backBufferHeight;

	//D3D Resources. 
	ID3D11Device*             d3dDevice;
	ID3D11DeviceContext*      d3dImmediateContext;
	IDXGISwapChain*           swapChain;
	ID3D11RenderTargetView*   backBufferRenderTargetView;
	ID3D11DepthStencilView*   depthStencilView;
	ID3D11ShaderResourceView* depthStencilSRV; //Used to render depth buffer after Z pre pass.
	D3D11_VIEWPORT            screenViewport;
	//For the uni computers, we can attempt to use a software
	//driver - this is horribly slow (and rather pointless as it
	//turns out)!
	D3D_DRIVER_TYPE d3dDriverType;

	//MSAA data
	//Is MSAA Enabled?
	bool msaaEnabled;
	//MSAA (4x) Quality - Max for your graphics card. We need to ask
	//the device what the quality level is for the backbuffer. Note: Any
	//render targets that we create MUST have the same MSAA settings (Sample count
	//and I belive quality) - though can (obviously)have different formats.
	UINT msaaQuality;

	//Function that inits direct3D - swapchain, depth/stencil buffer, render
	//targets etc. 
	void InitDirect3D(HWND mainWnd);

private:
	//Tile Based Forward Rendering data (Internal)
	//
	//Buffers - we dont *need* to keep a pointer to the buffers (Can release them as the
	//views increment the reference count as well - thus not destroying the resource)
	//,but we will do so just incase. 
	//
	//Buffer (Structured) for the Light List Buffer - Structured buffer filled with
	//our scene lights. Essentially, our top level buffer of the three we will use. The other
	//two will be filled via the Compute Shader (Read-Write buffers though Unordered
	//Access Views (UAVs))
	//
	//Aka, LLB
	ID3D11Buffer* lightListBuffer;
	//Rwad-Write (RW) buffer for the Light List Index Buffer. Second buffer in the hierarchy
	//that will be used to read which lights affect a given tile. (My report will have a more
	//detailed explanation). Depends on the screen size, thus will be resized on a resize event.
	//
	//Aka, LLIB
	ID3D11Buffer* lightListIndexBuffer;
	//Read-Write buffer for the Light List Start End Buffer. Bottom buffer (first sampled)
	//which indicate where to index in to the LLIB - Ie, the start index and the End index (starting
	//at 0 and ending at numLightsInTile-1) affecting this tile. We can work out the count of 
	//lights: (end-start) + 1.
	//
	//Again, depends on the screen size and thus will be resized whenever a resize event occurs.
	//
	//Aka, LLSEB
	ID3D11Buffer* lightListStartEndBuffer;
	//
	//Buffer to hold the maximum z value of all the pixels for a given tile. Thus, one entry
	//per tile. This will be obtained in the first pass of our compute shader.
	//
	//Aka, MTZB
	ID3D11Buffer* maxTileZBuffer;

	//
	//Resourece Views
	//
	//SRV to the Light List Buffer. Passed to shaders as input. 
	ID3D11ShaderResourceView* lightListBufferSRV;
	//UAV to the LLIB (Data filled (output) in the Compute Shader - hence UAV)
	ID3D11UnorderedAccessView* lightListIndexBufferUAV;
	//SRV to the LLIB (When fed in to shaders as input)
	ID3D11ShaderResourceView* lightListIndexBufferSRV;
	//UAV to the LLSEB (Again, data set in CS)
	ID3D11UnorderedAccessView* lightListStartEndBufferUAV;
	//SRV to the LLSEB (Fed in to shaders as inpit)
	ID3D11ShaderResourceView* lightListStartEndBufferSRV;
	//UAV to the MTZB - Read-write so therefore, pass this to the quad
	//effect when we want to render the max tile z buffer. for some unknown
	//reason, passing an SRV to this buffer doesnt work as intended. :S
	ID3D11UnorderedAccessView* maxTileZBufferUAV;
	//
	//UAVs/SRVs to the transparant LLIB(T) and LLSEB(T)
	ID3D11ShaderResourceView* lightListIndexBufferTSRV;
	ID3D11UnorderedAccessView* lightListIndexBufferTUAV;
	ID3D11ShaderResourceView* lightListStartEndBufferTSRV;
	ID3D11UnorderedAccessView* lightListStartEndBufferTUAV;

	//
	//Other data
	//
	//Number of lights in our light list buffer
	unsigned lightCount;
	//
	//Light Culling Effect pointer.
	TileForwardLightCullCSFX* tileCullCSEffect;

	//
	//Functions
	//
	//Resize (Or create) the Light List Index Buffer(LLIB) and LightListStartEndBuffer (LLSEB)
	//used within the tile based renderer. Both of these depend on the screen size
	//and thus need to be destroyed and recreated everytime a Resize event
	//occurs. 
	//
	//Also creates the associated views to the buffers. 
	//
	//Modified: 24/10/13 - Will also create LLIBT and LLSEBT (Transparant data)
	void CreateLLIBAndLLSEB();
	//Creates MaxTileZBuffer - Again, resizes based on the screen size. This buffer contains
	//the maximum z value found during the first pass in the compute shader (Light Cull Stage) for
	//each tile. Optionally, we have a texture2D for rendering (below) which contains better
	//comments for the idea - Note, we could have input a SRV to this buffer in the 
	//render call and used it to colour the quad, but I didn't realise this would be the route
	//I was going to go down (This is a highly experimental piece of code). Thankfully, 
	//you can turn this feature off completly so no memory has been wasted.
	void CreateMaxTileZBuffer();
	//
	//Works out how many tiles in the X and Y direction are needed to cover the entire
	//screen. Rounds up as we need to ensure that there is a thread per pixel (its okay if we
	//dispatch too many threads - No reads are preformed and more importantly no writes)
	//
	//Pass by reference - changes/sets the passed in paramaters to the required
	//values. 
	void ObtainTilesCount(float& x, float& y);

private:
	//Tile Based Forward Rendering Debug functionality (Internal)
	//
	//We will render the debug data via full screen quads. We need
	//to create one via this private function.
	//
	//Buffers
	ID3D11Buffer* quadVB;
	ID3D11Buffer* quadIB;
	//Buffer used to render the lights. Will be used in conjunction
	//with the instanced data buffer. Thus, for every instance, we will
	//extend this point in to a quad, make it face the camera, and colour.
	ID3D11Buffer* pointVB;
	//Instanced data
	ID3D11Buffer* lightsInstancedBuffer;
	//Effects
	TexturedFullScreenQuadFX* quadEffect;
	DebugDrawLightsFX* lightsDrawEffect;

	//Create the quad buffers.
	void CreateFullScreenQuad();
	//Function that creates our vertex buffer (for our point which will be
	//extended to a quad in the Geometry Shader per instance)
	//for the lights - used when we want to render the lights for the user
	//to see - Called once when we init the 
	void CreateLightPointVertexBuffer();
	//Creates instance data.
	void CreateLightVisInstanceBuffer(TileBasedLight* lightArr);

private:
	//Make this class a singleton class.
	//
	//Shared instance
	static GraphicsManager* instance;

	//Ensure the programmer uses my public functions.
	GraphicsManager();
	~GraphicsManager();
	GraphicsManager(GraphicsManager& other);
	GraphicsManager& operator=(GraphicsManager& other);
};